/*
 * Niiuzu
 * Copyright (C) 2011  Thierry Nowak
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.niiuzu.server.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;
import com.niiuzu.server.dao.FeedChannelDao;
import com.niiuzu.server.dao.exception.DaoException;
import com.niiuzu.server.dao.impl.dto.mapping.FeedChannelGroupMapper;
import com.niiuzu.server.dao.impl.dto.mapping.FeedChannelMapper;
import com.niiuzu.server.dao.impl.dto.object.DtoFeedChannel;
import com.niiuzu.server.dao.impl.dto.object.DtoFeedChannelGroup;
import com.niiuzu.server.dao.impl.dto.object.DtoUser;
import com.niiuzu.server.dao.impl.util.KeyUtil;
import com.niiuzu.shared.model.FeedChannel;
import com.niiuzu.shared.model.FeedChannelGroup;
import com.niiuzu.shared.model.User;

/**
 * Implémentation du gestionnaire des données des flux et des thèmes.
 */
public class FeedChannelDaoImpl extends AbstractDatastoreDao implements
		FeedChannelDao {

	private static final Logger LOGGER = Logger
			.getLogger(FeedChannelDaoImpl.class.getName());

	static {
		ObjectifyService.register(DtoFeedChannel.class);
		ObjectifyService.register(DtoFeedChannelGroup.class);
	}

	@Override
	public void createOrUpdateFeedChannel(FeedChannel feedChannel)
			throws DaoException {

		LOGGER.info("Début de la création ou mise à jour du flux "
				+ feedChannel);

		Objectify objectify = null;

		try {

			KeyUtil keyUtil = getKeyUtil();

			FeedChannelMapper mapper = getFeedChannelMapper();
			DtoFeedChannel dtoFeedChannel = mapper
					.mapSerializableToDto(feedChannel);

			objectify = createObjectify(true);
			objectify.put(dtoFeedChannel);

			// Place l'identifiant généré
			String uid = keyUtil.getEncodedKeyFor(dtoFeedChannel);
			feedChannel.setUid(uid);

			commit(objectify);

		} catch (Exception e) {
			rollback(objectify);

			LOGGER.log(Level.SEVERE,
					"Erreur lors de la création ou de la mise à jour du flux "
							+ feedChannel, e);
			throw new DaoException(e);
		} finally {
			LOGGER
					.info("Fin de création ou mise à jour du flux "
							+ feedChannel);
		}
	}

	@Override
	public void createOrUpdateFeedChannelGroup(FeedChannelGroup feedChannelGroup)
			throws DaoException {

		LOGGER.info("Début de la création ou mise à jour du thème "
				+ feedChannelGroup);

		Objectify objectify = null;

		try {

			KeyUtil keyUtil = getKeyUtil();

			FeedChannelGroupMapper mapper = getFeedChannelGroupMapper();
			DtoFeedChannelGroup dtoFeedChannelGroup = mapper
					.mapSerializableToDto(feedChannelGroup);

			objectify = createObjectify(true);
			objectify.put(dtoFeedChannelGroup);

			// Place l'identifiant généré
			String uid = keyUtil.getEncodedKeyFor(dtoFeedChannelGroup);
			feedChannelGroup.setUid(uid);

			commit(objectify);

		} catch (Exception e) {
			rollback(objectify);

			LOGGER.log(Level.SEVERE,
					"Erreur lors de la création ou de la mise à jour du thème "
							+ feedChannelGroup, e);
			throw new DaoException(e);
		} finally {
			LOGGER.info("Fin de création ou mise à jour du thème "
					+ feedChannelGroup);
		}
	}

	@Override
	public void deleteFeedChannel(FeedChannel feedChannel) throws DaoException {

		LOGGER.info("Début de la suppression du flux " + feedChannel);

		Objectify objectify = null;

		try {

			FeedChannelMapper mapper = getFeedChannelMapper();
			DtoFeedChannel dtoFeedChannel = mapper
					.mapSerializableToDto(feedChannel);

			objectify = createObjectify(true);
			objectify.delete(dtoFeedChannel);

			commit(objectify);

		} catch (Exception e) {
			rollback(objectify);

			LOGGER.log(Level.SEVERE, "Erreur lors de la suppression du flux "
					+ feedChannel, e);
			throw new DaoException(e);
		} finally {
			LOGGER.info("Fin de la suppression du flux " + feedChannel);
		}
	}

	@Override
	public void deleteFeedChannelGroup(FeedChannelGroup feedChannelGroup)
			throws DaoException {

		LOGGER.info("Début de la suppression du thème " + feedChannelGroup);

		Objectify inputObjectify = null;

		try {

			FeedChannelGroupMapper mapper = getFeedChannelGroupMapper();
			DtoFeedChannelGroup dtoFeedChannelGroup = mapper
					.mapSerializableToDto(feedChannelGroup);

			Objectify outputObjectify = createObjectify(false);

			// Récupération des flux liés
			Query<DtoFeedChannel> query = outputObjectify
					.query(DtoFeedChannel.class);
			query = query.ancestor(dtoFeedChannelGroup);
			List<DtoFeedChannel> linkedDtoChannels = query.list();

			inputObjectify = createObjectify(true);

			inputObjectify.delete(linkedDtoChannels);
			inputObjectify.delete(dtoFeedChannelGroup);

			commit(inputObjectify);

		} catch (Exception e) {
			rollback(inputObjectify);

			LOGGER.log(Level.SEVERE, "Erreur lors de la suppression du thème "
					+ feedChannelGroup, e);
			throw new DaoException(e);
		} finally {
			LOGGER.info("Fin de suppression du thème " + feedChannelGroup);
		}
	}

	@Override
	public List<FeedChannel> getChannels(User owner) throws DaoException {

		LOGGER.info("Début de la récupération des flux");

		List<FeedChannel> channels = null;

		try {

			Objectify objectify = createObjectify(false);
			KeyUtil keyUtil = getKeyUtil();

			String userUid = owner.getUid();

			Key<DtoUser> userKey = keyUtil.convertStringToKey(userUid,
					DtoUser.class);

			Query<DtoFeedChannelGroup> queryGroup;
			queryGroup = objectify.query(DtoFeedChannelGroup.class);
			List<Key<DtoFeedChannelGroup>> dtoChannelGroupKeys;
			dtoChannelGroupKeys = queryGroup.ancestor(userKey).listKeys();

			List<DtoFeedChannel> dtoChannels = new ArrayList<DtoFeedChannel>();
			for (Key<DtoFeedChannelGroup> groupKey : dtoChannelGroupKeys) {

				Query<DtoFeedChannel> query;
				query = objectify.query(DtoFeedChannel.class);
				List<DtoFeedChannel> currentChannelList;
				currentChannelList = query.ancestor(groupKey).list();

				dtoChannels.addAll(currentChannelList);
			}

			FeedChannelMapper mapper = getFeedChannelMapper();
			channels = mapper.mapDtoToSerializableList(dtoChannels);

		} catch (Exception e) {
			LOGGER.log(Level.SEVERE, "Erreur lors de la récupération des flux",
					e);
			throw new DaoException(e);
		} finally {
			LOGGER.info("Fin de la récupération des flux");
		}

		return channels;
	}

	@Override
	public List<FeedChannelGroup> getChannelGroups(User owner)
			throws DaoException {

		LOGGER.info("Début de la récupération des thèmes");

		List<FeedChannelGroup> channelGroups = null;

		try {

			Objectify objectify = createObjectify(false);
			KeyUtil keyUtil = getKeyUtil();

			String userUid = owner.getUid();
			Key<DtoUser> userKey = keyUtil.convertStringToKey(userUid,
					DtoUser.class);

			Query<DtoFeedChannelGroup> query = objectify
					.query(DtoFeedChannelGroup.class);
			List<DtoFeedChannelGroup> dtoChannelGroups = query
					.ancestor(userKey).list();

			FeedChannelGroupMapper mapper = getFeedChannelGroupMapper();
			channelGroups = mapper.mapDtoToSerializableList(dtoChannelGroups);

		} catch (Exception e) {
			LOGGER.log(Level.SEVERE,
					"Erreur lors de la récupération des thèmes", e);
			throw new DaoException(e);
		} finally {
			LOGGER.info("Fin de la récupération des thèmes");
		}

		return channelGroups;
	}

	/**
	 * Récupère une instance de {@link FeedChannelMapper}.
	 */
	private FeedChannelMapper getFeedChannelMapper() {
		if (mockedChannelMapper != null) {
			return mockedChannelMapper;
		}
		return new FeedChannelMapper();
	}

	/**
	 * Récupère une instance de {@link FeedChannelGroupMapper}.
	 */
	private FeedChannelGroupMapper getFeedChannelGroupMapper() {
		if (mockedChannelGroupMapper != null) {
			return mockedChannelGroupMapper;
		}
		return new FeedChannelGroupMapper();
	}

	/* ********************************************************************** */
	/* ********************************************************************** */
	/* ********************************************************************** */

	private FeedChannelMapper mockedChannelMapper;
	private FeedChannelGroupMapper mockedChannelGroupMapper;

	/**
	 * JUnit - Définit un mock pour {@link FeedChannelMapper}.
	 */
	void setMockedFeedChannelMapper(FeedChannelMapper mockedChannelMapper) {
		this.mockedChannelMapper = mockedChannelMapper;
	}

	/**
	 * JUnit - Définit un mock pour {@link FeedChannelGroupMapper}.
	 */
	void setMockedFeedChannelGroupMapper(
			FeedChannelGroupMapper mockedChannelGroupMapper) {
		this.mockedChannelGroupMapper = mockedChannelGroupMapper;
	}
}
